package smashedapples.air.sqlite
{
	import flash.data.SQLConnection;
	import flash.data.SQLStatement;
	import flash.events.SQLErrorEvent;
	import flash.events.SQLEvent;
	import flash.filesystem.File;

	import mx.events.FlexEvent;

	import smashedapples.air.sqlite.commands.SQLiteDeleteCommand;
	import smashedapples.air.sqlite.commands.SQLiteSaveCommand;
	import smashedapples.air.sqlite.events.SQLiteDeleteEvent;
	import smashedapples.air.sqlite.events.SQLiteSaveEvent;
	import smashedapples.core.NonVisualComponent;


	[Event( name="open", type = "flash.events.SQLEvent" )]
	[Event( name="result", type = "flash.events.SQLEvent" )]
	[Event( name="error", type = "flash.events.SQLErrorEvent" )]

	[Event( name="save", type = "smashedapples.air.sqlite.events.SQLiteSaveEvent" )]
	[Event( name="Delete", type = "smashedapples.air.sqlite.events.SQLiteDeleteEvent" )]

	public class SQLiteDB extends NonVisualComponent
	{
		public function SQLiteDB()
		{
			super();
			this.addEventListener( FlexEvent.CREATION_COMPLETE, handleCreationCompleteEvent );
			this.addEventListener( SQLiteSaveEvent.SAVE, handleSaveResultEvent );
			this.addEventListener( SQLiteDeleteEvent.DELETE_BY_KEYS, handleDeleteResultEvent );
		}

		[ArrayElementType( "smashedapples.air.sqlite.SQLiteTable" )]

		public var tables:Array = [];

		[ArrayElementType( "smashedapples.air.sqlite.SQLiteView" )]

		public var views:Array = [];

		[Inspectable( enumeration="true,false", defaultValue = "false" )]

		public var debug:Boolean = false;

		public var fileName:String;

		private var database:File;

		public var connection:SQLConnection;

		private var _queries:Array = [];

		[ArrayElementType( "smashedapples.air.sqlite.SQLiteQuery" )]

		public function get queries():Array
		{
			return this._queries;
		}

		public function set queries( value:Array ):void
		{
			for each( var query:SQLiteQuery in value )
			{
				addQuery( query );
			}
		}

		public function addQuery( query:SQLiteQuery ):void
		{
			query.db = this;
			this.queries.push( query );
		}


		public function handleCreationCompleteEvent( event:FlexEvent ):void
		{
			if( this.fileName != null )
			{
				this.initializeDatabase( this.fileName );
			}
		}


		public function initializeDatabase( fileName:String ):void
		{
			this.database = File.applicationStorageDirectory.resolvePath( fileName );
			this.connection = new SQLConnection();
			this.connection.addEventListener( SQLEvent.OPEN, handleSqlOpenEvent );
			this.connection.addEventListener( SQLErrorEvent.ERROR, handleSqlErrorEvent );
			this.connection.openAsync( this.database );
		}

		private function handleSqlOpenEvent( event:SQLEvent ):void
		{
			this.dispatchEvent( event );
			this.createTables();
		}

		private function handleSqlErrorEvent( event:SQLErrorEvent ):void
		{
			this.dispatchEvent( event );
		}

		public function createTables():void
		{
			for each( var table:SQLiteTable in this.tables )
			{
				createTable( table );
			}
		}

		public function createTable( table:SQLiteTable ):void
		{
			var statement:SQLStatement = new SQLStatement();
			statement.sqlConnection = this.connection;
			statement.text = SQLiteCommandBuilder.createTableStatement( table );
			addToDebug( statement.text );
			statement.execute();
		}


		public function getTableByName( name:String ):SQLiteTable
		{
			var table:SQLiteTable;

			for each( var item:SQLiteTable in this.tables )
			{
				if( item.name == name )
				{
					table = item;
					break;
				}
			}

			return table;
		}


		public function deleteByKeys( tableName:String, keys:Object = null, result:Function = null, fault:Function = null ):void
		{
			var command:SQLiteDeleteCommand = new SQLiteDeleteCommand( this, tableName, keys );
			var statement:SQLStatement = new SQLStatement();
			statement.sqlConnection = this.connection;

			statement.text = SQLiteCommandBuilder.createDeleteStatement( command );
			statement.addEventListener( SQLEvent.RESULT, command.resultHandler );
			statement.addEventListener( SQLErrorEvent.ERROR, command.faultHandler );

			//add typed parameters
			statement = SQLiteCommandBuilder.setStatementParameters( statement, command.parameters );

			//debug
			addToDebug({ sql:statement.text, keys:statement.parameters });


			statement.execute();
		}

		public function save( tableName:String, parameters:Object = null, result:Function = null, fault:Function = null ):void
		{
			var command:SQLiteSaveCommand = new SQLiteSaveCommand( this, tableName, parameters, result, fault );

			var statement:SQLStatement = new SQLStatement();
			statement.sqlConnection = this.connection;

			// create sql text
			statement.text = SQLiteCommandBuilder.createSaveStatement( command );

			// add named parameters
			if( command.isInsert )
			{
				// on insert we exclude the primary key name
				statement = SQLiteCommandBuilder.setStatementParameters( statement, command.parameters, command.pkName );
			}
			else
			{
				//on update we want all parameters typed
				statement = SQLiteCommandBuilder.setStatementParameters( statement, command.parameters );
			}


			// add result/fault handlers
			statement.addEventListener( SQLEvent.RESULT, command.resultHandler );
			statement.addEventListener( SQLErrorEvent.ERROR, command.faultHandler );

			//debug
			addToDebug({ sql:statement.text, parameters:statement.parameters });

			//execute	
			statement.execute();
		}



		public function addToDebug( value:* ):void
		{
			if( this.debug )
				Debug.push( this, value, true );
		}


		public function autoUpdateQueries( tableName:String ):void
		{
			for each( var query:SQLiteQuery in this.queries )
			{
				if( query.tableName == tableName )
				{
					query.executeAutoUpdate();
				}
			}
		}

		private function handleSaveResultEvent( event:SQLiteSaveEvent ):void
		{
			autoUpdateQueries( event.response.table.name );
		}

		private function handleDeleteResultEvent( event:SQLiteDeleteEvent ):void
		{
			autoUpdateQueries( event.response.table.name );
		}

	}
}